iT邦幫忙

2024 iThome 鐵人賽

DAY 6
0

Day26 要來做個有下拉選單的導覽列

資料

const navItems: NavItem[] = [
  { name: "About", dropdown: ["Team", "History", "Contact"] },
  { name: "Services", dropdown: ["Design", "Development", "Consulting"] },
  { name: "Contact", dropdown: ["Email", "Phone", "Location"] },
];
  const [activeDropdown, setActiveDropdown] = useState<number | null>(null);
  const [dropdownStyles, setDropdownStyles] = useState<DropdownStyles>({});
  const navRef = useRef<HTMLElement | null>(null);
  const backgroundRef = useRef<HTMLDivElement | null>(null);

滑鼠動作

  const handleMouseEnter = useCallback(
    (e: React.MouseEvent<HTMLLIElement>, index: number) => {
      const dropdown = (e.target as HTMLElement).querySelector(".dropdown");
      if (!dropdown || !navRef.current) {
        return;
      }

      setActiveDropdown(index);

      const navBounds = navRef.current.getBoundingClientRect();
      const dropdownBounds = dropdown.getBoundingClientRect();

      const styles: DropdownStyles = {
        width: `${dropdownBounds.width}px`,
        height: `${dropdownBounds.height}px`,
        transform: `translate(${dropdownBounds.left - navBounds.left}px, ${
          dropdownBounds.top - navBounds.top
        }px)`,
      };

      setDropdownStyles(styles);
    },
    []
  );

  const handleMouseLeave = useCallback(() => {
    setActiveDropdown(null);
  }, []);

畫面結構

  return (
    <nav ref={navRef} className="relative p-4 bg-white shadow-md">
      <div
        ref={backgroundRef}
        className="absolute bg-white transition-all duration-150 ease-out opacity-0"
        style={{
          ...dropdownStyles,
          opacity: activeDropdown !== null ? 1 : 0,
          visibility: activeDropdown !== null ? "visible" : "hidden",
        }}
      />
      <ul className="flex space-x-4">
        {navItems.map((item, index) => (
          <li
            key={item.name}
            className="relative"
            onMouseEnter={(e) => handleMouseEnter(e, index)}
            onMouseLeave={handleMouseLeave}
          >
            <a href="#" className="text-gray-700 hover:text-gray-900">
              {item.name}
            </a>
            <div
              className={`dropdown absolute left-0 top-full mt-2 w-48 bg-white shadow-lg rounded-md overflow-hidden transition-all duration-150 ease-out ${
                activeDropdown === index
                  ? "opacity-100 visible"
                  : "opacity-0 invisible"
              }`}
            >
              <ul className="py-2">
                {item.dropdown.map((subItem) => (
                  <li key={subItem}>
                    <a
                      href="#"
                      className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
                    >
                      {subItem}
                    </a>
                  </li>
                ))}
              </ul>
            </div>
          </li>
        ))}
      </ul>
    </nav>
  );

DEMO

https://codesandbox.io/p/devbox/nh87pg


上一篇
[Day25]_Event-Capture-Propagation-Bubbling-and-Once
下一篇
[Day27]_Click-and-Drag
系列文
React30——用 React 探索 JavaScript30 的魅力30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言